package netty.builder;

import io.netty.channel.*;
import io.netty.channel.socket.SocketChannel;
import org.springframework.util.Assert;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public abstract class NettyBuilderBase<T extends NettyBuilderBase> {
    protected Class<? extends Channel> channelClass;
    protected final ChannelHandler defaultChildHandler;
    protected ChannelHandler channelInitializer;
    protected List<ChannelHandler> pipelineHandlers = new ArrayList<>();
    protected Map<ChannelOption, Object> options = new HashMap<>();

    public NettyBuilderBase() {
        defaultChildHandler = new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel ch) throws Exception {
                pipelineHandlers.forEach(h -> ch.pipeline().addLast(h));
            }
        };
    }

    /**
     * 可选配置。指定channelClass时，注意channelClass与EventLoopGroup匹配。
     * @param channelClass
     * @return
     */
    public T channelClass(Class<? extends ServerChannel> channelClass) {
        this.channelClass = channelClass;
        return self();
    }

    public <O> T option(ChannelOption<O> option, O value) {
        options.put(option, value);
        return self();
    }

    /**
     * 向ChannelPipeline添加ChannelHandler，以添加的顺序加入ChannelPipeline。与channelInitializer方法不可同时使用。
     * @param handler
     * @return
     */
    public T addPipelineHandler(ChannelHandler handler) {
        Assert.isNull(channelInitializer, "已指定ChannelInitializer。channelInitializer和pipelineHandlers只能二选一。");
        pipelineHandlers.add(handler);
        return self();
    }

    /**
     * 指定ChannelHandler。与addPipelineHandler方法不可同时使用。
     * @param initializer
     * @return
     */
    public T channelInitializer(ChannelHandler initializer) {
        Assert.isTrue(pipelineHandlers.size() == 0, "已添加pipelineHandler。channelInitializer和pipelineHandlers只能二选一。");
        this.channelInitializer = initializer;
        return self();
    }

    @SuppressWarnings("unchecked")
    private T self() {
        return (T) this;
    }
}
